/*******************************************************************************
 * Copyright (c) baifeiq.
 * 
 * @date            @author         @brief
 * 2021-07-12       baifeiq         First version: start the first task of ByOs,
 *                                  and monitor the os_task_oder.
 * 2021-12-30       baifeiq         Reorganize the coding style.
 * 2022-01-11       baifeiq         Integrate task readiness table and state into 
 *                                  os_sys_param.
 */

#include "os_task.h"
#include "os_scheduler.h"
#include "os_config.h"
#include "os_common.h"
#include "os_define.h"
#include "os_list.h"
#include "os_event.h"

/* Interface of running the first task, which is define in os_context.s */
extern void os_run_first_task(void);

/* Interface of creatting the PendSVC interrupt, which is define in os_context.s */
extern void os_enter_PendSVC(void);

extern os_sys_para_t os_sys_param;

os_task_t *task_current;
os_task_t *task_next;

os_task_t os_task_idle;
os_uint32_t os_task_idle_stact[1024/4];

void os_task_idle_main(void *param)
{
	while(1){;}
}

/**
 * @brief Task switch request interface.
 */
void os_sched(void)
{
    os_uint32_t os_interrupt = os_critical_enter();

    if (os_sys_param.scheduler_lock > 0)
    {
        os_critical_quit(os_interrupt);
        return;
    }

    task_next = os_sys_param.task_table[os_get_bitmap_lowest(os_sys_param.ready_table)]; 

    os_sys_param_update(task_next, task_next, task_current);
    
    if (task_current != task_next)
    {
        os_enter_PendSVC();
    }    

    os_critical_quit(os_interrupt);
}

/**
 * @brief Enable task scheduling lock 
 */
void ost_sched_enable()
{
    os_uint32_t os_interrupt = os_critical_enter();

    if (os_sys_param.scheduler_lock < 255)
    {
        os_sys_param.scheduler_lock++;
    }
    
    os_critical_quit(os_interrupt);
}

/**
 * @brief Disable task scheduling lock 
 */
void ost_sched_disable(void)
{
    os_uint32_t os_interrupt = os_critical_enter();

    if (os_sys_param.scheduler_lock > 0)
    {
        os_sys_param.scheduler_lock--;
        if (0 == os_sys_param.scheduler_lock)
        {
            os_sched();
        }
    }

    os_critical_quit(os_interrupt);
}

/*  //Keil MDK
__asm void PendSV_Handler(void)              
{
    IMPORT task_current
    IMPORT task_next

    MRS R0, PSP 
    CBZ R0, PendSVHander_nosave
    
    STMDB   R0!, {R4-R11}
    LDR R1, =task_current
    LDR R1, [R1]
    STR R0, [R1]
    NOP
    
PendSVHander_nosave
    LDR R0, =task_current
    LDR R1, =task_next
    LDR R2, [R1]
    STR R2, [R0]

    LDR R0, [R2]
    LDMIA R0!,  {R4-R11}

    MSR PSP, R0
    ORR LR, LR, #0X04
    BX LR
}*/

/*  //gcc
//os_task_t *task_current asm("task_current");       //gcc
//os_task_t *task_next asm("task_next");             //gcc
void PendSV_Handler(void)   
{
    asm("EXTERN task_current"); 
    asm("EXTERN task_next");


    asm("MRS R0, PSP"); 
    asm("CBZ R0, PendSVHander_nosave");
    
    asm("STMDB   R0!, {R4-R11}");
    asm("LDR R1, =task_current");
    //asm volatile("bx %0" : : "r" (task_current));
    asm("LDR R1, [R1]");
    asm("STR R0, [R1]");
    asm("NOP");
    
    asm("PendSVHander_nosave:");
    asm("LDR R0, =task_current");
    //asm volatile("bx %0" : : "r" (task_current));
    asm("LDR R1, =task_next");

    asm("LDR R2, [R1]");
    asm("STR R2, [R0]");

    asm("LDR R0, [R2]");
    asm("LDMIA R0!,  {R4-R11}");

    asm("MSR PSP, R0");
    asm("ORR LR, LR, #0X04");
    asm("BX LR");
}*/

/**
 * @brief System initialization function.
 */
void ost_startup_init(void)
{
    ost_task_init(&os_task_idle, os_task_idle_main, (void *)0x33333333, 31, 
             &os_task_idle_stact[1024/4]);
    
    os_list_insert_after(&os_sys_param.task_delay, &os_task_idle.list_delay);
 
    task_next = &os_task_idle;

    // sys_tick_init();
    os_run_first_task();
}

/**
 * @brief Task scheduling manager.
 */
os_int32_t os_scheduler(void)
{
    os_list_t *p_list_current = &os_sys_param.task_delay;

    os_systick();

    OS_NULL_CHECK(p_list_current);

    if (os_list_isempty(p_list_current))
    {
        return -2;
    }
    
    p_list_current = p_list_current->next;

	while ((unsigned)p_list_current != (unsigned)&os_sys_param.task_delay)
    {
        /* step1 */
        os_task_t *p_task_current = os_list_entry(p_list_current, os_task_t, list_delay);

        /* step2 */
		p_list_current = p_list_current->next;

        /* step3 */
        if (!(p_task_current->timer--))
        {
            if (p_task_current->event)
            {
                ost_event_remove(p_task_current, OS_NULL, 0);
            }

            ost_delay_wake(p_task_current);
        }
    }

	os_sched();

    return 0;
}
